package com.fwc.fw.service;

import com.fwc.fw.annotation.LookupDomo;
import com.fwc.fw.annotation.LookupField;
import com.fwc.fw.domo.AbstractPersistableDomo;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.jdo.Extent;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.annotations.Persistent;

import org.apache.commons.beanutils.BeanUtils;
import org.springframework.orm.jdo.support.JdoDaoSupport;

import com.google.appengine.api.datastore.Key;

public class DataStoreServiceImpl extends JdoDaoSupport implements DataStoreService {

    public List<AbstractPersistableDomo> findAll(Class clazz) {
    	return findAll(clazz,false);
    }
    public List<AbstractPersistableDomo> findAll(Class clazz, String order) {
    	return findAll(clazz,false,order);
    }

    public List<AbstractPersistableDomo> findAll(Class clazz, boolean applyDefaultFilter) {
    	return findAll(clazz,applyDefaultFilter,null);
    }
    public List<AbstractPersistableDomo> findAll(Class clazz, boolean applyDefaultFilter, String order) {

    	PersistenceManager pm = getPersistenceManager();

    	String defaultFilter = "";
    	if(applyDefaultFilter) {
    	    defaultFilter = getDefaultClassFilter(clazz);
        }

    	try {
    	  Query query = pm.newQuery(clazz);
	  	    if(null!=defaultFilter && !defaultFilter.isEmpty()) {
		    	query.setFilter(defaultFilter);
		    }
	  	    if(null!=order && !order.isEmpty()) {
	  	    	query.setOrdering(order);
	  	    }
	  	  List<AbstractPersistableDomo> results = (List<AbstractPersistableDomo>) query.execute();
    	  results.size();
    	  return results;
    	}catch(Exception e) {
    		e.printStackTrace();
    		return null;
	    } finally {
	      releasePersistenceManager(pm);
	    }
    }
    
	@Override
	public Collection<AbstractPersistableDomo> search(Class clazz, String q) {
		return search(clazz, q, true);
	}
	public Collection<AbstractPersistableDomo> search(Class clazz, String q, boolean orFields) {
       	PersistenceManager pm = getPersistenceManager();
    	Extent e = pm.getExtent(clazz);
    	
    	Map<String, AbstractPersistableDomo> mergedResults = new HashMap<String, AbstractPersistableDomo>();
    	
    	if(!q.contains("%")) {
    		q += "%";
    	}
    	
    	for(int i=0; i<clazz.getDeclaredFields().length; i++) {
    		Field f = clazz.getDeclaredFields()[i];
    		if(f.getAnnotation(LookupField.class)!=null) {
    			Query query = pm.newQuery(e);
    			Map<String,String> paramVals = new HashMap<String, String>();
    			StringBuffer filter = new StringBuffer();
    			StringBuffer params = new StringBuffer();
    			filter.append(f.getName() + ".matches(" + f.getName()+"Param)");
    			/*
        		if(orFields) {
        			filter.append(" || ");
        		} else {
        			filter.append(" && ");
        		}
        		*/
        		params.append(f.getType().getName() + " "+f.getName()+"Param");
        		paramVals.put(f.getName()+"Param", q);
	    	    String defaultFilter = getDefaultClassFilter(clazz);
	    	    if(null!=defaultFilter && !defaultFilter.isEmpty()) {
	    	    	filter.append(" && " + defaultFilter);
	    	    }
	    	    query.setFilter(filter.toString().replaceAll(" (&&|\\|\\|) $",""));
	    	    
	    	    query.declareParameters(params.toString().replaceAll(", $", ""));
	    	    try {
	    	    	List<AbstractPersistableDomo> results = (List<AbstractPersistableDomo>) query.executeWithMap(paramVals);
	    	    	for(AbstractPersistableDomo d : results) {
	    	    		mergedResults.put(d.getKey(), d);
	    	    	}
	    	    } catch(Exception ex) {
	    	    	ex.printStackTrace();
	    	    } finally {
	    	        query.closeAll();
	    	    }
    		}
    	}

    	releasePersistenceManager(pm);

    	/*
	    try {
	    	List<AbstractPersistableDomo> results = (List<AbstractPersistableDomo>) query.executeWithMap(paramVals);
	    	return results;
	    } catch(Exception ex) {
	    	ex.printStackTrace();
	    } finally {
	        query.closeAll();
	        releasePersistenceManager(pm);
	    }
	    */
//	    return null;
    	return mergedResults.values();
	}

    public AbstractPersistableDomo save(AbstractPersistableDomo o) {
    	return (AbstractPersistableDomo) getJdoTemplate().makePersistent(o);
    }

    public void remove(AbstractPersistableDomo domo) {
    	getJdoTemplate().deletePersistent(domo);
    }

    public AbstractPersistableDomo find(Class clazz, Long id) throws Exception {
       	PersistenceManager pm = getPersistenceManager();
       	AbstractPersistableDomo domo = null;
    	try {
    	  domo = (AbstractPersistableDomo) pm.detachCopy(pm.getObjectById(clazz,id));
	    } finally {
	      releasePersistenceManager(pm);
	    }
	    if(null!=domo)
	    	return findUnownedRelatives(domo);
	    else return null;
    }
    public AbstractPersistableDomo find(Class clazz, String id) throws Exception  {
       	PersistenceManager pm = getPersistenceManager();
       	AbstractPersistableDomo domo = null;
    	try {
    	  domo = (AbstractPersistableDomo) pm.detachCopy(pm.getObjectById(clazz,id));
	    } finally {
	      releasePersistenceManager(pm);
	    }
	    if(null!=domo)
	    	return findUnownedRelatives(domo);
	    else return null;
    }
    public AbstractPersistableDomo find(Class clazz, Key id) throws Exception {
       	PersistenceManager pm = getPersistenceManager();
       	AbstractPersistableDomo domo = null;
    	try {
    	  domo = (AbstractPersistableDomo) pm.detachCopy(pm.getObjectById(clazz,id));
	    } finally {
	      releasePersistenceManager(pm);
	    }
	    if(null!=domo)
	    	return findUnownedRelatives(domo);
	    else return null;
    }

    public AbstractPersistableDomo findByEmail(Class clazz, String email) {
       	PersistenceManager pm = getPersistenceManager();
    	Extent e = pm.getExtent(clazz);
    	Query query = pm.newQuery(e);
	    query.setFilter("email == emailParam");
	    query.declareParameters("String emailParam");

	    try {
	    	List<AbstractPersistableDomo> results = (List<AbstractPersistableDomo>) query.execute(email);
	        if(null!=results && !results.isEmpty()) return results.get(0);
	    } catch(Exception ex) {
	    	ex.printStackTrace();
	    } finally {
	        query.closeAll();
	        releasePersistenceManager(pm);
	    }
	    return null;
    }
    
    public AbstractPersistableDomo findUnownedRelatives(AbstractPersistableDomo domo) throws Exception {
    	for(int i=0; i < domo.getClass().getDeclaredFields().length; i++) {
    		Field f = domo.getClass().getDeclaredFields()[i];
    		String relFieldName = f.getName().replace("Key", "");

    		Field relField = null;
    		try {
    			relField = domo.getClass().getDeclaredField(relFieldName);
        		if(f.getName().matches(".+Key") && f.getType().equals(Key.class)) {
    				String methName = "get"+f.getName().substring(0,1).toUpperCase() + f.getName().substring(1);
    				Key key = (Key) domo.getClass().getMethod(methName).invoke(domo);
        			AbstractPersistableDomo rel = find(relField.getType(),key);
        			BeanUtils.setProperty(domo, relField.getName(), rel);
        		}
    		} catch(NoSuchFieldException e) {}

    	}
    	return domo;
    }

	@Override
	public List<AbstractPersistableDomo> findAllWithUnowned(Class clazz) {
		return findAllWithUnowned(clazz, null);
	}
	public List<AbstractPersistableDomo> findAllWithUnowned(Class clazz, String order) {
		List<AbstractPersistableDomo> domos = findAll(clazz, order);
		for(AbstractPersistableDomo d : domos) {
			try {
				findUnownedRelatives(d);
			} catch (Exception e) {
			}
		}
		return domos;
	}
	
	private String getDefaultClassFilter(Class clazz) {
	    if(null!=clazz.getAnnotation(LookupDomo.class)) {
	    	LookupDomo a = (LookupDomo) clazz.getAnnotation(LookupDomo.class);
	    	return a.filter();
	    }
	    return "";
	}


}